package com.neato.ui.comp;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;

import com.neato.ui.comp.listener.ListViewDragListener;

public class DragableListView extends ListView{

	private ListViewDragListener dragListener;
	
	private int dragPointY;  //the drag event point
	private int coordY_Offset;
	
	private int dragedItemIndex;
	private int firstDragedItemIndex;
	
	private Rect dragingRect = new Rect();
	private ImageView dragedItemView;
	private Bitmap dragedBitmap;
	
	private WindowManager winManager;
	private WindowManager.LayoutParams dragedWinParams;
	
	private int DRAGED_BG_COLOR = 0x00000000;
	
	private int mItemHeightHalf = 32;
	private int mItemHeightNormal = 64;
	private int mItemHeightExpanded = 128;
	
	private int scaledTouchSlop;//判断滑动的一个距离 
	private int upScrollBounce;//拖动的时候，开始向上滚动的边界     
	private int downScrollBounce;//拖动的时候，开始向下滚动的边界  
	    
	private MotionEvent motionEvent = null;
	
	public DragableListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}

	public DragableListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public DragableListView(Context context) {
		super(context);
		init();
	}
	
	private void init(){
		scaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();  
		winManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 
	}
	
	@Override
	public boolean onInterceptTouchEvent(final MotionEvent evt) {
		if (evt.getAction() == MotionEvent.ACTION_DOWN){
			OnItemLongClick(evt);
		}
		return super.onInterceptTouchEvent(evt);
	}
	
	public void OnItemLongClick(final MotionEvent ev)
	{
		this.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener()
		{
			@Override
			public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
					int arg2, long arg3){
				
				afterLongClick(ev);
				return true;
			};
		});
	}
	
	private void afterLongClick(MotionEvent evt){
		int x = (int) evt.getX();
		int y = (int) evt.getY();
		int itemNum = this.pointToPosition(x, y);

		if (itemNum == AdapterView.INVALID_POSITION) {
			return;
		}
		dragedItemIndex = itemNum;
		firstDragedItemIndex = itemNum;
		
		int index = itemNum - getFirstVisiblePosition();
		Log.i("getChild", "--"+getFirstVisiblePosition()+"--"+itemNum+"--"+index);
		ViewGroup item = (ViewGroup) getChildAt(index);
	
		dragPointY = y - item.getTop();
		coordY_Offset = ((int) evt.getRawY()) - y;

		upScrollBounce = Math.min(y-scaledTouchSlop, getHeight()/3);  
        downScrollBounce = Math.max(y+scaledTouchSlop, getHeight()*2/3); 
         
		//item.destroyDrawingCache();
		item.setDrawingCacheEnabled(true);
		// Create a copy of the drawing cache so that it does not get recycled
		// by the framework when the list tries to clean up memory
		Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
		startDragging(bitmap, y);
	}
	
	private void startDragging(Bitmap bitmap, int y) {
		stopDragging();

		ImageView view = new ImageView(getContext());
		view.setBackgroundColor(DRAGED_BG_COLOR);
		view.setImageBitmap(bitmap);
		dragedBitmap = bitmap;
		dragedItemView = view;
		
		dragedWinParams = createWindowLayout(y);
		winManager.addView(view, dragedWinParams);
	}
	
	private void stopDragging() {
		if (dragedItemView != null) {
			winManager.removeView(dragedItemView);
			dragedItemView.setImageDrawable(null);
			dragedItemView = null;
		}
		if (dragedBitmap != null) {
			dragedBitmap.recycle();
			dragedBitmap = null;
		}
	}
	
	private  WindowManager.LayoutParams createWindowLayout(int currentY){
		WindowManager.LayoutParams params = new WindowManager.LayoutParams();
		params.gravity = Gravity.TOP;
		params.x = 0;
		params.y = currentY - dragPointY + coordY_Offset;

		params.height = WindowManager.LayoutParams.WRAP_CONTENT;
		params.width = WindowManager.LayoutParams.WRAP_CONTENT;
		params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
				| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
				| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
				| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
		params.format = PixelFormat.TRANSLUCENT;
		params.windowAnimations = 0;
		return params;
	}

	@Override
	public boolean onTouchEvent(MotionEvent evt) {
		
		if (dragedItemView != null && dragedItemIndex != AdapterView.INVALID_POSITION) {
			switch (evt.getAction()){
				case MotionEvent.ACTION_MOVE:
					onDrag(evt);
					break;
				case MotionEvent.ACTION_UP:
					onDrop(evt);
					break;
			}
		}
		return super.onTouchEvent(evt);
	}
	
	private boolean onDrag(MotionEvent evt){
		int x = (int) evt.getX();
		int y = (int) evt.getY();
		draggingView((int)evt.getRawY());
		
		scrollViewOnDrag(x,y);
				
		int itemNum = getItemIndex(y);
		if (itemNum > 0 && itemNum != dragedItemIndex) {
			dragedItemIndex = itemNum;
			doExpansionViews();
			return true;
		}
		return false;
	}
	
	private void draggingView( int y) {
		float alpha = 1.0f;
		dragedWinParams.alpha = alpha;
		dragedWinParams.y = y - dragPointY;
		winManager.updateViewLayout(dragedItemView, dragedWinParams);
	}
	
	private void scrollViewOnDrag(int x, int y){
		//为了避免滑动到分割线的时候，返回-1的问题  
        int itemNum = super.pointToPosition(x, y);  
        if(itemNum != INVALID_POSITION){  
            this.dragedItemIndex = itemNum;  
        }  
          
        //滚动  
        int scrollHeight = 0;  
        if(y < upScrollBounce){  
            scrollHeight = 8;//定义向上滚动8个像素，如果可以向上滚动的话  
        }else if(y > downScrollBounce){  
            scrollHeight = -8;//定义向下滚动8个像素，，如果可以向上滚动的话  
        }  
          
        if(scrollHeight != 0){  
        	int y_distance  = getChildAt(dragedItemIndex-getFirstVisiblePosition()).getTop()+scrollHeight;
            //真正滚动的方法setSelectionFromTop()  
            setSelectionFromTop(dragedItemIndex, y_distance);  
        }  
	}
	
	private boolean onDrop(MotionEvent evt) {
		int y = (int)evt.getY();
		//为了避免滑动到分割线的时候，返回-1的问题  
		int tempPosition = super.pointToPosition(0, y);  
		if(tempPosition!=INVALID_POSITION){  
			dragedItemIndex = tempPosition;  
		}  

		//超出边界处理  
		if(y<getChildAt(1).getTop()){  
		    //超出上边界  
			dragedItemIndex = 1;  
		} else if (y>getChildAt(getChildCount()-1).getBottom()){  
	   	    //超出下边界  
			dragedItemIndex = getAdapter().getCount()-1;  
		}  

		//
		dragedItemView.getDrawingRect(dragingRect);
		stopDragging();
		if (dragedItemIndex >= 0 && dragedItemIndex < getCount()) {
			dragListener.drop(firstDragedItemIndex, dragedItemIndex);
		}
		//unExpandViews(false);
		return true;
	}
	
	private int getItemIndex(int y) {
		int offsetY = y - dragPointY - mItemHeightHalf;
		int pos = this.pointToPosition(0, offsetY);
		if (pos>=0 && pos<=firstDragedItemIndex) {
			pos += 1;
		} else if (offsetY < 0) {
			pos = 0;
		}
		return pos;
	}
	
	/**
	 * pointToPosition() doesn't consider invisible views,<\br>
	 * but we need to, so override this method  
	 */
	@Override
	public int pointToPosition(int x, int y) {
		final int count = getChildCount();
		for (int i = count - 1; i >= 0; i--) {
			final View child = getChildAt(i);
			child.getHitRect(dragingRect);
			if (dragingRect.contains(x, y)) {
				return getFirstVisiblePosition() + i;
			}
		}
		return INVALID_POSITION;
	}
	
	/**
	 * Adjust visibility and size to make it appear as though an item is being
	 * dragged around and other items are making room for it;
	 * </br>
	 * <ul>
	 * <li>If dropping the item would result in it still being in the same place, then make the
	 * dragged list item's size normal, but make the item invisible. </li>
	 * <li>If the dragged list item is still on screen, 
	 * make it as small as possible and expand the item below the insert point.</li>
	 * <li> If the dragged item is not on screen,
	 * only expand the item below the current insert point.</li>
	 * </ul>
	 */
	private void doExpansionViews() {
		int childnum = dragedItemIndex - getFirstVisiblePosition();
		if (dragedItemIndex > firstDragedItemIndex) {
			childnum++;
		}
		//get the upper child item of the draged item
		View upperChild = getChildAt(firstDragedItemIndex - getFirstVisiblePosition());

		for (int i = 0;; i++) {
			View view = getChildAt(i);
			if (view == null) {
				break;
			}
			int height = mItemHeightNormal;
			int visibility = View.VISIBLE;
			if (view.equals(upperChild)) {
				// processing the item that is being dragged
				if (dragedItemIndex == firstDragedItemIndex) {
					// hovering over the original location
					visibility = View.INVISIBLE;
				} else {
					// not hovering over it
					height = 1;
				}
			} else if (i == childnum) {
				if (dragedItemIndex < getCount() - 1) {
					height = mItemHeightExpanded;
				}
			}
			setItemViewParams(view,height,visibility);
		}
	}
	
	/**
	 * Restore size and visibility for all list items
	 */
	private void unExpandViews(boolean deletion) {
		for (int i=0; ;i++) {
			View view = getChildAt(i);
			if (view == null) {
				if (deletion) {
					// HACK force update of mItemCount
					int position = getFirstVisiblePosition();
					int y = getChildAt(0).getTop();
					setAdapter(getAdapter());
					setSelectionFromTop(position, y);
					// end hack
				}
				layoutChildren(); // force children to be recreated where needed
				view = getChildAt(i);
				if (view == null) {
					break;
				}
			}
			setItemViewParams(view,mItemHeightNormal,View.VISIBLE);
		}
	}
	
	private void setItemViewParams(View view, int h, int visibility){
		ViewGroup.LayoutParams params = view.getLayoutParams();
		params.height = h;
		view.setLayoutParams(params);
		view.setVisibility(visibility);
	}
	
	public void setOnDragListener(ListViewDragListener dragListener) {
		this.dragListener = dragListener;
	}
	
}
